# cython: language_level=3
import os
import threading
import time

import uvicorn
from dotenv import load_dotenv
from fastapi import FastAPI, Response
from fastapi.middleware.cors import CORSMiddleware
from starlette.concurrency import iterate_in_threadpool
from starlette.requests import Request

import app.scheduler as scheduler
import json
from app.db.redis.invitation import init_invitation
from app.db.sql.operator import init_operator
from app.router import v2
from app.router.token import check_authorization
from common.database import engine, Base
from common.http import error_response
from common.log import get_logger
from common.redis import MyRedis

load_dotenv()

logger = get_logger("server")

REDIS_HOST = os.getenv("REDIS_HOST", "localhost")
REDIS_PORT = os.getenv("REDIS_PORT", 6379)
REDIS_DB = os.getenv("REDIS_DB", 0)
REDIS_PASSWORD = os.getenv("REDIS_PASSWORD", None)
REDIS_PREFIX = os.getenv("REDIS_PREFIX", "fastapi")


def create_app() -> FastAPI:
    logger.info("=" * 100)
    Base.metadata.create_all(bind=engine)  # 数据库初始化，如果没有库或者表，会自动创建
    init_invitation()
    init_operator()
    _ = FastAPI()
    _.add_middleware(
        CORSMiddleware,
        allow_origins=["*"],
        allow_credentials=True,
        allow_methods=["*"],
        allow_headers=["*"],
    )

    @_.middleware("http")
    async def auth_verify(request: Request, call_next):
        path: str = request.get("path")
        redis = MyRedis(REDIS_HOST, REDIS_PORT, REDIS_DB, REDIS_PASSWORD, REDIS_PREFIX)

        logger.info(path)

        if not path.startswith("/api/v2"):
            return await call_next(request)

        authorization = request.headers.get("Authorization")

        if not authorization:
            return Response(status_code=200, content=json.dumps(error_response(401, "Unauthorized")))

        try:
            authorization_json = json.loads(authorization)
            app_id = authorization_json.get("appId")
            token = authorization_json.get("token")
            sign = authorization_json.get("sign")

            if not check_authorization(app_id, token, sign, redis):
                return Response(status_code=200, content=json.dumps(error_response(401, "Unauthorized")))

        except json.JSONDecodeError:
            return Response(status_code=200, content=json.dumps(error_response(401, "Unauthorized")))
        return await call_next(request)

    @_.middleware("http")
    async def log_requests(request, call_next):
        start_time = time.time()

        response = await call_next(request)

        process_time = (time.time() - start_time) * 1000
        formatted_process_time = '{0:.2f}'.format(process_time)
        logger.info(f"request={request.url}")
        logger.info(f"completed_in={formatted_process_time}ms status_code={response.status_code}")

        return response

    @_.middleware("http")
    async def log_response(request: Request, call_next):
        response = await call_next(request)
        response_body = [chunk async for chunk in response.body_iterator]
        response.body_iterator = iterate_in_threadpool(iter(response_body))
        logger.info(f"response_body={response_body[0].decode()}")
        return response

    _.include_router(v2, prefix="/api")
    return _


def main():
    app = create_app()
    threading.Thread(target=scheduler.start, daemon=True).start()
    if os.getenv("IPV6"):
        host = "::"
    else:
        host = os.getenv("HOST", "0.0.0.0")
    port = int(os.getenv("PORT", "8000"))
    uvicorn.run(app, host=host, port=port)


if __name__ == "__main__":
    main()
